using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using Unity.VisualScripting;
using UnityEngine;

namespace EGEngine
{
    public class NetworkModule : MonoBehaviour
    {
        [SerializeField] private string serverIp;
        [SerializeField] private int serverPort;
        [SerializeField] private int timeout;
        [SerializeField] private int reconnectCount;

        private static string _serverIp;
        private static int _serverPort;
        private static int _timeout;
        private static int _reconnectCount;
        private static int _currentReconnectCount;

        private static Socket _socket;
        private static Thread _clientThread;
        public static Action OnConnected;
        public static Action<bool> OnDisconnected;
        private static byte[] _buffer;
        private static int _bufferPosition;
        private static INetworkMessagePacker _packer;
        private static ConcurrentQueue<NetworkMessage> _receiveMessages;
        private static ConcurrentQueue<IMessage> _sendMessages;

        public static ENetworkState State;

        public void Init(INetworkMessagePacker packer)
        {
            _receiveMessages = new ConcurrentQueue<NetworkMessage>();
            _sendMessages = new ConcurrentQueue<IMessage>();
            _reconnectCount = reconnectCount;
            _timeout = timeout;
            _packer = packer;
            _serverIp = serverIp;
            _serverPort = serverPort;
            _buffer = new byte[1048576];
        }

        public static async Task<bool> ConnectToServerAsync()
        {
            _socket?.Close();
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.ReceiveTimeout = _timeout;
            _socket.SendTimeout = _timeout;
            LogModule.Log($"正在连接{_serverIp}:{_serverPort}...");
            State = ENetworkState.Connecting;
            await _socket.ConnectAsync(_serverIp, _serverPort);
            if (_socket.Connected)
            {
                LogModule.Log($"连接成功{_serverIp}:{_serverPort},开始接收消息!");
                State = ENetworkState.Connected;
                OnConnected?.Invoke();
                _currentReconnectCount = 0;
                Receive();
                return true;
            }

            State = ENetworkState.UnConnect;
            LogModule.Log($"连接失败{_serverIp}:{_serverPort}!");

            while (_currentReconnectCount < _reconnectCount)
            {
                LogModule.Log($"重连尝试{_currentReconnectCount + 1}/{_reconnectCount} {_serverIp}:{_serverPort}...");
                State = ENetworkState.Connecting;
                bool res = await ConnectToServerAsync();
                if (res)
                {
                    LogModule.Log($"连接成功{_serverIp}:{_serverPort},开始接收消息!");
                    State = ENetworkState.Connected;
                    OnConnected?.Invoke();
                    _currentReconnectCount = 0;
                    Receive();
                    return true;
                }

                State = ENetworkState.UnConnect;
                _currentReconnectCount++;
                LogModule.Log($"重连失败{_currentReconnectCount + 1}/{_reconnectCount} {_serverIp}:{_serverPort}!");
            }

            State = ENetworkState.ReconnectFailed;
            return false;
        }

        public static void DisconnectFromServer()
        {
            State = ENetworkState.UnConnect;
            _bufferPosition = 0;
            if (_socket is { Connected: true })
            {
                _socket.Close();
                State = ENetworkState.Disconnected;
                OnDisconnected?.Invoke(true);
            }
        }

        public static async Task Send(IMessage message)
        {
            byte[] sendBytes = _packer.Pack(message);
            try
            {
                await _socket.SendAsync(sendBytes, SocketFlags.None);
            }
            catch (Exception e)
            {
                State = ENetworkState.Disconnected;
                _socket.Close();
                OnDisconnected?.Invoke(false);
            }
        }

        private static async Task Receive()
        {
            int count = 0;
            try
            {
                var segment = new ArraySegment<byte>(_buffer, _bufferPosition, _buffer.Length - _bufferPosition);
                count = await _socket.ReceiveAsync(segment, SocketFlags.None);
            }
            catch (Exception e)
            {
                State = ENetworkState.Disconnected;
                _socket.Close();
                OnDisconnected?.Invoke(false);
                return;
            }

            _bufferPosition += count;
            var (messages, useCount) = _packer.Unpack(_buffer, _bufferPosition);
            if (messages is { Count: > 0 })
            {
                foreach (var message in messages)
                {
                    _receiveMessages.Enqueue(message);
                }

                Array.Copy(_buffer, useCount, _buffer, 0, _bufferPosition - useCount);
                _bufferPosition -= useCount;
            }

            await Receive();
        }
    }
}